//
//  LBGraphics.cpp
//  OpenGLGame
//
//  Created by Nguyen Cong Huy on 11/8/12.
//  Copyright (c) 2012 UIT. All rights reserved.
//

#include <iostream>
#include "LBGraphics.h"
#include "LBFramebuffer.h"
#include "LBTexture.h"
#include "LBProgram.h"
#include "Matrix.h"
#include "LBUtils.h"

LBGraphics* LBGraphics::instance = 0;

LBGraphics::LBGraphics(){
    mProjection = new Matrix();
}

void LBGraphics::init(){
    char vShaderStr[] = ""
    "   precision lowp float;  "
    
    "   attribute highp vec4 position;   "
    "   attribute vec2 texCoor;    "
    "   uniform highp mat4 mWorld;"
    "   uniform highp mat4 mProjection;"
    "   varying vec2 vtexCoor; "
    "   varying highp vec4 vposition; "
    
    "   void main()    "
    "   {  "
    "       gl_Position = mProjection * mWorld * position; "
    "       vtexCoor=texCoor;   "
    "       vposition = gl_Position;"
    "   }  ";
    
	char fShaderStr[] = ""
    "   precision lowp float;  "
    "   uniform sampler2D texture; "
    "   varying vec2 vtexCoor; "
    "   void main()    "
    "   {  "
    "       gl_FragColor=texture2D(texture,vtexCoor);   "
    "   }  "
    "";

    
    memcpy(vShaderStrDefault, vShaderStr, sizeof(vShaderStr));
    memcpy(fShaderStrDefault, fShaderStr, sizeof(fShaderStr));
    
    defaultProgram = new LBProgram(vShaderStr, fShaderStr);

    positionAttribute = defaultProgram->attributeIndex("position");
    texCoorAttribute = defaultProgram->attributeIndex("texCoor");
    textureUniform = defaultProgram->uniformIndex("texture");
    
    program = defaultProgram;

    char vShaderDrawStr[] = ""
		"   precision lowp float;"

		"   attribute highp vec4 position;"
		"   uniform highp mat4 mWorld;"
		"   uniform highp mat4 mProjection;"
		"	uniform vec4 color;"
		"	varying vec4 vcolor;"

		"   void main()    "
		"   {  "
		"       gl_Position = mProjection * mWorld * position; "
		"       vcolor=color;"
		"   }  ";

	char fShaderDrawStr[] = ""
		"   precision lowp float;  "
		"	varying vec4 vcolor;"
		"   void main()    "
		"   {  "
		"       gl_FragColor=vcolor;"
		"   }  "
		"";

	drawProgram = new LBProgram(vShaderDrawStr, fShaderDrawStr);
    
	glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glDisable(GL_DEPTH_TEST);
	
	glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFB);

	setRectColor(1.0f, 0.0f, 0.0f, 1.0f);
	mWorld = new Matrix();
}

LBGraphics* LBGraphics::getInstance(){
    if(instance == 0)
        instance = new LBGraphics();
    return  instance;
}

LBProgram* LBGraphics::getDefaultProgram(){
    return defaultProgram;
}

int LBGraphics::getWidth(){
    return  width;
}

int LBGraphics::getHeight(){
    return  height;
}

void LBGraphics::setWindowsSize(int width, int height){
    this->width = width;
    this->height = height;
    mProjection->Ortho(0, width, 0, height, -10, 10);
}

GLuint LBGraphics::getPositionAttribute(){
    return program->attributeIndex("position");
}

GLuint LBGraphics::getTexCoorAttribute(){
    return program->attributeIndex("texCoor");
}

GLuint LBGraphics::getTextureUniform(){
    return program->uniformIndex("texture");
}

GLuint LBGraphics::getWorldUniform(){
    return program->uniformIndex("mWorld");
}

GLuint LBGraphics::getProjectionUniform(){
    return program->uniformIndex("mProjection");
}

char* LBGraphics::getDefaultVShaderStr(){
    return vShaderStrDefault;
}

char* LBGraphics::getDefaultFShaderStr(){
    return fShaderStrDefault;
}

void LBGraphics::deleteInstance(){
    DELETE_SAFELY(instance);
}

LBGraphics::~LBGraphics(){
    DELETE_SAFELY(defaultProgram);
    DELETE_SAFELY(mProjection);
}

void LBGraphics::useProgram(LBProgram* program){
    this->program = program;
    program->use();
}

LBProgram* LBGraphics::getProgram(){
    return program;
}

GLuint LBGraphics::attributeIndex(const char* attributeName){
    return program->attributeIndex(attributeName);
}

GLuint LBGraphics::uniformIndex(const char* uniformName){
    return program->uniformIndex(uniformName);
}

void LBGraphics::useDefaultProgram(){
    useProgram(defaultProgram);
}

void LBGraphics::setDefaulFrameBuffer(GLint framebuffer){
    defaultFB = framebuffer;
}

GLint LBGraphics::getDefaultFrameBuffer(){
    return defaultFB;
}

void LBGraphics::viewport(int x, int y, int width, int height){
    glViewport(x, y, width, height);
}

void LBGraphics::renderToScreen(){
    glBindFramebuffer(GL_FRAMEBUFFER, LBGraphics::getInstance()->getDefaultFrameBuffer());
    //glBindFramebuffer(GL_FRAMEBUFFER, 0);

    
    setWindowsSize(widthScreen, heightScreen);
    viewport(xScreenViewport, yScreenViewport, widthScreenViewport, heightScreenViewport);
}

void LBGraphics::setScreenSizeAndViewportInRealDevice(int widthScreen, int heightScreen, int xScreenViewport, int yScreenViewport, int widthScreenViewport, int heightScreenViewport){
    this->widthScreen = widthScreen;
    this->heightScreen = heightScreen;
    this->xScreenViewport = xScreenViewport;
    this->yScreenViewport = yScreenViewport;
    this->widthScreenViewport = widthScreenViewport;
    this->heightScreenViewport = heightScreenViewport;
    setWindowsSize(widthScreen, heightScreen);
    renderToScreen();
}

void LBGraphics::clear(float red, float green, float blue){
    glClearColor(red, green, blue, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
}

Matrix* LBGraphics::getProjectionMatrix(){
    return mProjection;
}

void LBGraphics::drawRect(LBRect rect){
	LBGraphics *g = LBGraphics::getInstance();
	float left = rect.x;
	float right = left + rect.width;
	float top = rect.y;
	float bottom = top + rect.height;
	rectVertices[0] = left;
	rectVertices[1] = bottom;
	rectVertices[2] = right;
	rectVertices[3] = bottom;
	rectVertices[4] = right;
	rectVertices[5] = top;
	rectVertices[6] = left;
	rectVertices[7] = top;
    rectVertices[8] = left;
    rectVertices[9] = bottom;

    drawProgram->use();
	int positionAttribute = drawProgram->attributeIndex("position");
	int mWorldUniform = drawProgram->uniformIndex("mWorld");
	int mProjectionUniform = drawProgram->uniformIndex("mProjection");
	int colorUniform = drawProgram->uniformIndex("color");

	glUniformMatrix4fv(mWorldUniform, 1, GL_FALSE, &mWorld->m[0][0]);
	glUniformMatrix4fv(mProjectionUniform, 1, GL_FALSE, &(g->getProjectionMatrix()->m[0][0]));
	glVertexAttribPointer(positionAttribute, 2, GL_FLOAT, GL_FALSE, 0, rectVertices);
	//glUniform4f(colorUniform, 4, GL_FLOAT, GL_FALSE, 0, rectColors);
    glUniform4fv(colorUniform, 1, rectColors);

	glEnableVertexAttribArray(positionAttribute);
	glEnableVertexAttribArray(texCoorAttribute);

	glDrawArrays(GL_LINE_STRIP, 0, 5);

	glDisableVertexAttribArray(positionAttribute);
	glDisableVertexAttribArray(texCoorAttribute);

}

void LBGraphics::setRectColor(float red, float green, float blue, float alpha){

	drawProgram->use();

	rectColors[0] = red;
	rectColors[1] = green;
	rectColors[2] = blue;
	rectColors[3] = alpha;

}
